import os
import time
import json
import logging
from typing import Optional
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from dotenv import load_dotenv
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException

# 配置日志以输出脚本执行信息 (Configure logging to output information about the script's execution)
logging.basicConfig(
    level=logging.INFO, 
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

class PostPublish:
    def __init__(self):
        """用环境变量初始化内容发布器 (Initialize the content poster with environment variables)"""
        load_dotenv()

        # 脚本运行所需的环境变量列表 (List of required environment variables for the script to function)
        self.required_vars = [
            'GENERATION_ENDPOINT', 
            'PLATFORM_URL', 
            'LOGIN_URL', 
            'PHONE_EMAIL', 
            'PASSWORD'
        ]
        self._validate_env_vars()

        # 设置Selenium WebDriver选项以无头模式运行 (Set up Selenium WebDriver options for headless operation)
        chrome_options = webdriver.ChromeOptions()
        chrome_options.add_argument('--no-sandbox')
        chrome_options.add_argument('--disable-dev-shm-usage')
        chrome_options.add_argument('--disable-extensions')
        chrome_options.add_argument('--disable-gpu')
        
        # 用指定选项初始化WebDriver (Initialize WebDriver with the specified options)
        self.driver = webdriver.Chrome(
            service=ChromeService(ChromeDriverManager().install()),
            options=chrome_options
        )
        
        # 设置WebDriver的隐式等待时间 (Set implicit wait time for WebDriver)
        self.driver.implicitly_wait(10)
        
        # 初始化用于HTTP请求的会话 (Initialize a requests session for HTTP requests)
        self.session = requests.Session()
        self.token = None

    def _validate_env_vars(self) -> None:
        """验证所有必需的环境变量是否已设置 (Validate that all required environment variables are set)"""
        # 检查是否有缺失的环境变量 (Check for any missing environment variables)
        missing_vars = [var for var in self.required_vars if not os.getenv(var)]
        if missing_vars:
            raise ValueError(f"Missing required environment variables: {', '.join(missing_vars)}")

    def find_element_safely(self, by, value, timeout=10):
        """
        安全地查找元素并进行错误处理和日志记录 (Safely find an element with error handling and logging)
        
        参数 (Args):
            by: Selenium By 定位策略 (Selenium By locator strategy)
            value: 定位值 (Locator value)
            timeout: 最大等待时间 (Maximum wait time)
        
        返回 (Returns):
            WebElement 或 None (WebElement or None)
        """
        try:
            # 等待元素可见并返回 (Wait for the element to be visible and return it)
            element = WebDriverWait(self.driver, timeout).until(
                EC.visibility_of_element_located((by, value))
            )
            return element
        except (TimeoutException, NoSuchElementException) as e:
            # 如果未找到元素则记录错误 (Log an error if the element is not found)
            logger.error(f"Could not find element {value}: {e}")
            return None

    def login(self) -> bool:
        """
        使用Selenium登录网站 (Log in to the website using Selenium)
        
        返回 (Returns):
            bool: 如果登录成功则为True，否则为False (True if login is successful, False otherwise)
        """
        login_url = os.getenv('LOGIN_URL')

        try:
            # 导航到登录URL (Navigate to the login URL)
            self.driver.get(login_url)

            # 定位并点击账户登录选项 (Locate and click the account login option)
            account_login_option = WebDriverWait(self.driver, 10).until(
                EC.element_to_be_clickable((By.XPATH, "//*[@id='app']/div/div/div[2]/div[2]/ul/li[2]/a/span[2]"))
            )
            account_login_option.click()

            # 定位电话/电子邮件和密码输入字段 (Locate the phone/email and password input fields)
            phone_email_input = WebDriverWait(self.driver, 10).until(
                EC.visibility_of_element_located((By.XPATH, "//*[@id='app']/div/div/div[2]/div[2]/form/div[1]/input"))
            )
            password_input = WebDriverWait(self.driver, 10).until(
                EC.visibility_of_element_located((By.XPATH, "//*[@id='app']/div/div/div[2]/div[2]/form/div[2]/input"))
            )

            # 定位并点击登录按钮 (Locate and click the login button)
            login_button = WebDriverWait(self.driver, 10).until(
                EC.element_to_be_clickable((By.XPATH, "//*[@id='app']/div/div/div[2]/div[2]/button"))
            )

            # 输入凭据并点击登录按钮 (Enter the credentials and click the login button)
            phone_email_input.send_keys(os.getenv('PHONE_EMAIL'))
            password_input.send_keys(os.getenv('PASSWORD'))
            login_button.click()

            # 通过检查特定元素验证登录成功 (Verify successful login by checking for a specific element)
            WebDriverWait(self.driver, 10).until(
                EC.presence_of_element_located((By.XPATH, "//*[@id='homeWrap']/div[1]/div/div[1]/div/textarea"))
            )

            # 存储会话cookie以供将来请求使用 (Store session cookies for future requests)
            cookies = self.driver.get_cookies()
            for cookie in cookies:
                self.session.cookies.set(cookie['name'], cookie['value'], domain=cookie['domain'])

            logger.info("登录成功 (Successfully logged in)")
            return True

        except Exception as e:
            # 如果登录失败则记录错误 (Log an error if login fails)
            logger.error(f"Login failed: {e}")
            return False

    def fetch_content(self) -> Optional[str]:
        """从指定端点获取生成的内容 (Fetch generated content from the specified endpoint)"""
        endpoint = os.getenv('GENERATION_ENDPOINT')

        try:
            # 向内容生成端点发出GET请求 (Make a GET request to the content generation endpoint)
            response = self.session.get(endpoint, timeout=2000)
            response.raise_for_status()

            # 处理响应内容 (Process the response content)
            content = response.text.strip()
            if not content:
                logger.warning("获取到空内容 (Fetched empty content)")
                return None

            logger.info(f"成功获取内容 (长度: {len(content)} 字符) (Successfully fetched content (length: {len(content)} chars))")
            logger.info(f"获取的内容: {content} (Fetched content: {content})")
            return content

        except requests.RequestException as e:
            # 如果内容获取失败则记录错误 (Log an error if content fetching fails)
            logger.error(f"Content fetching failed: {e}")
            return None

    def publish_post_on_platform(self, content: str) -> bool:
        """
        使用Selenium将内容发布到平台 (Post content to the platform using Selenium)
        """
        if not self.session.cookies:
                logger.error("没有会话cookie。请先登录 (No session cookies. Please login first.)")
                return False

        platform_url = os.getenv('PLATFORM_POST_URL')

        try:
            logger.info("导航到平台URL (Navigating to platform URL)")
            self.driver.get(platform_url)

            # 定位并点击发布按钮 (Locate and click the post button)
            post_button = WebDriverWait(self.driver, 10).until(
                EC.element_to_be_clickable((By.XPATH, "//*[@id='app']/div[2]/div[1]/div/div[1]/div/div/div[3]/div/div/div/button"))
            )
            post_button.click()

            logger.info("等待发布字段可见 (Waiting for the post field to be visible)")
            post_input = WebDriverWait(self.driver, 20).until(
                EC.visibility_of_element_located((By.XPATH, "//*[@id='app']/div[4]/div[1]/div/div[2]/div[1]/div/textarea"))
            )
            
            # 加载要发布的内容 (Load the content to be posted)
            content_data = json.loads(content)
            logger.info(f"要发布的内容: {content_data['post']} (Post to be published: {content_data['post']})")
            post_input.clear()
            post_input.send_keys(content_data['post'])

            logger.info("等待'发送'按钮可点击 (Waiting for the 'Send' button to be clickable)")
            send_button = WebDriverWait(self.driver, 10).until(
                EC.element_to_be_clickable((By.XPATH, "//*[@id='app']/div[4]/div[1]/div/div[2]/div[4]/div/div[5]/button"))
            )
            logger.info("点击'发送'按钮 (Clicking the 'Send' button)")
            send_button.click()

            logger.info("内容成功发布到平台 (Content posted to the platform successfully)")
            return True

        except Exception as e:
            # 如果发布失败则记录错误 (Log an error if posting fails)
            logger.error(f"Content posting to the platform failed: {e}", exc_info=True)
            logger.debug(f"Content attempted to post: {content}")
            return False

    def run(self) -> bool:
        """执行完整的发布工作流程 (Execute the full post publish workflow)"""
        # 尝试登录 (Attempt login)
        if not self.login():
            return False

        # 获取内容 (Fetch content)
        content = self.fetch_content()
        if not content:
            return False

        # 将内容发布到平台 (Post content to the platform)
        if not self.publish_post_on_platform(content):
            return False

        return True

def main():
    """脚本的主要入口点 (Main entry point for the script)"""
    poster = PostPublish()
    posting_interval = os.getenv('POSTING_INTERVAL')

    if posting_interval:
        posting_interval = int(posting_interval)

        while True:
            try:
                # 运行内容发布工作流程 (Run the content posting workflow)
                success = poster.run()
                if success:
                    logger.info("发布工作流程成功完成 (Post publish workflow completed successfully)")
                else:
                    logger.error("发布工作流程失败 (Post publish workflow failed)")
            except Exception as e:
                # 记录任何意外错误 (Log any unexpected errors)
                logger.error(f"Unexpected error: {e}")

            # 等待指定的间隔时间后再次运行工作流程 (Wait for the specified interval before running the workflow again)
            logger.info(f"等待 {posting_interval} 分钟... (Waiting for {posting_interval} minutes...)")
            time.sleep(posting_interval * 60)
    else:
        # 如果未设置POSTING_INTERVAL环境变量，则运行一次工作流程 (Run the workflow once if the POSTING_INTERVAL environment variable is not set)
        try:
            success = poster.run()
            if success:
                logger.info("发布工作流程成功完成 (Post publish workflow completed successfully)")
            else:
                logger.error("发布工作流程失败 (Post publish workflow failed)")
        except Exception as e:
            logger.error(f"意外错误: {e} (Unexpected error: {e})")

        exit(0)

if __name__ == "__main__":
    main()
